home *** CD-ROM | disk | FTP | other *** search
/ Aminet 1 (Walnut Creek) / Aminet - June 1993 [Walnut Creek].iso / aminet / comm / misc / zedzap05.lha / utils.c < prev    next >
C/C++ Source or Header  |  1992-11-27  |  34KB  |  973 lines

  1. /*  Utils.c: Miscellaneous support routines for xprzmodem.library;
  2.     Version 2.10, 12 February 1991, by Rick Huebner.
  3.     Released to the Public Domain; do as you like with this code.  */
  4.  
  5.  
  6. #include <proto/all.h>
  7. #include <exec/types.h>
  8. #include <exec/memory.h>
  9. #include <ctype.h>
  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. #include <string.h>
  13. #include "xproto.h"
  14. #include "zmodem.h"
  15. #include "xprzmodem.h"
  16.  
  17. long OPEN_LIBS        = 0;    /* the amount of times this library is being */
  18.                               /* accessed...  remeber, the dos.library may */
  19.                               /* only  be closed and opened once, so it is */
  20.                               /* very important that we know when the last */
  21.                               /* program finishes accessing the library!   */
  22.  
  23. /* Transfer options to use if XProtocolSetup not called */
  24. struct SetupVars Default_Config = {
  25.   NULL, NULL, 0,
  26.   { "C" }, { "N" }, { "16" }, { "0" }, { "10" },
  27.   { "N" }, { "N" }, { "Y" }, { "N" }, { "Y" }, { "0"}, { "8192" },
  28.   { "Y" }, { "N" }, { "Y" }, { "ENV:" }, { "" }
  29. };
  30.  
  31. #ifdef DEBUGLOG
  32. UBYTE DebugName[] = "Log:XDebug.log";
  33. void *DebugLog = NULL;
  34. #endif
  35.  
  36.  
  37. /* Called by comm program to set transfer options */
  38. long __saveds XProtocolSetup(struct XPR_IO *io) {
  39.   struct SetupVars *sv, tempsv;
  40.   struct xpr_option *option_ptrs[18], *optr, xo_hdr, xo_t, xo_o, xo_b, xo_f;
  41.   struct xpr_option xo_e, xo_s, xo_r, xo_a, xo_d, xo_k, xo_p, xo_m, xo_n, xo_c;
  42.   struct xpr_option xo_u, xo_v, xo_l;
  43.   UBYTE buf[512], *p, *bufback;
  44.   long i, len;
  45.  
  46.   /* Allocate memory for transfer options string */
  47.   if (!(sv = (void *)io->xpr_data)) {
  48.     io->xpr_data = AllocMem((long)sizeof(struct SetupVars),MEMF_CLEAR);
  49.     if (!(sv = (void *)io->xpr_data)) {
  50.       ioerr(io,"Not enough memory");
  51.       return XPRS_FAILURE;
  52.     }
  53.     /* Start out with default options; merge user changes into defaults */
  54.     *sv = Default_Config;
  55.   }
  56.  
  57.   /* If options string passed by comm prog, use it; else prompt user */
  58.   if (io->xpr_filename)
  59.     strcpy(buf,io->xpr_filename);
  60.   else {
  61.     /* If xpr_options() implemented by comm program, use it */
  62.     if (io->xpr_extension >= 1 && io->xpr_options) {
  63.       /* Let user edit temp copy of options so we can ignore invalid entries */
  64.       /* Have to init all this crud the hard way 'cause it's got to be on the
  65.          stack in order to maintain reentrancy */
  66.       tempsv = *sv;
  67.       xo_hdr.xpro_description = "ZedZap Options:";
  68.       xo_hdr.xpro_type = XPRO_HEADER;
  69.       xo_hdr.xpro_value = NULL;
  70.       xo_hdr.xpro_length = 0;
  71.       option_ptrs[0] = &xo_hdr;
  72.       xo_t.xpro_description = "Text Mode (Y,N,?,C):";
  73.       xo_t.xpro_type = XPRO_STRING;
  74.       xo_t.xpro_value = tempsv.option_t;
  75.       xo_t.xpro_length = sizeof(tempsv.option_t);
  76.       option_ptrs[1] = &xo_t;
  77.       xo_o.xpro_description = "Overwrite Mode (Y,N,R,S):";
  78.       xo_o.xpro_type = XPRO_STRING;
  79.       xo_o.xpro_value = tempsv.option_o;
  80.       xo_o.xpro_length = sizeof(tempsv.option_o);
  81.       option_ptrs[2] = &xo_o;
  82.       xo_b.xpro_description = "I/O Buffer Size (KB):";
  83.       xo_b.xpro_type = XPRO_LONG;
  84.       xo_b.xpro_value = tempsv.option_b;
  85.       xo_b.xpro_length = sizeof(tempsv.option_b);
  86.       option_ptrs[3] = &xo_b;
  87.       xo_f.xpro_description = "Frame Size (bytes):";
  88.       xo_f.xpro_type = XPRO_LONG;
  89.       xo_f.xpro_value = tempsv.option_f;
  90.       xo_f.xpro_length = sizeof(tempsv.option_f);
  91.       option_ptrs[4] = &xo_f;
  92.       xo_e.xpro_description = "Error Limit:";
  93.       xo_e.xpro_type = XPRO_LONG;
  94.       xo_e.xpro_value = tempsv.option_e;
  95.       xo_e.xpro_length = sizeof(tempsv.option_e);
  96.       option_ptrs[5] = &xo_e;
  97.       xo_a.xpro_description = "Auto-Activate Receiver:";
  98.       xo_a.xpro_type = XPRO_BOOLEAN;
  99.       xo_a.xpro_value = tempsv.option_a;
  100.       xo_a.xpro_length = sizeof(tempsv.option_a);
  101.       option_ptrs[6] = &xo_a;
  102.       xo_d.xpro_description = "Delete After Sending:";
  103.       xo_d.xpro_type = XPRO_BOOLEAN;
  104.       xo_d.xpro_value = tempsv.option_d;
  105.       xo_d.xpro_length = sizeof(tempsv.option_d);
  106.       option_ptrs[7] = &xo_d;
  107.       xo_k.xpro_description = "Keep Partial Files:";
  108.       xo_k.xpro_type = XPRO_BOOLEAN;
  109.       xo_k.xpro_value = tempsv.option_k;
  110.       xo_k.xpro_length = sizeof(tempsv.option_k);
  111.       option_ptrs[8] = &xo_k;
  112.       xo_s.xpro_description = "Send Full Path:";
  113.       xo_s.xpro_type = XPRO_BOOLEAN;
  114.       xo_s.xpro_value = tempsv.option_s;
  115.       xo_s.xpro_length = sizeof(tempsv.option_s);
  116.       option_ptrs[9] = &xo_s;
  117.       xo_r.xpro_description = "Use Received Path:";
  118.       xo_r.xpro_type = XPRO_BOOLEAN;
  119.       xo_r.xpro_value = tempsv.option_r;
  120.       xo_r.xpro_length = sizeof(tempsv.option_r);
  121.       option_ptrs[10] = &xo_r;
  122.       xo_p.xpro_description = "Default Receive Path:";
  123.       xo_p.xpro_type = XPRO_STRING;
  124.       xo_p.xpro_value = tempsv.option_p;
  125.       xo_p.xpro_length = sizeof(tempsv.option_p);
  126.       option_ptrs[11] = &xo_p;
  127.       xo_c.xpro_description = "Connect BPS Rate:";
  128.       xo_c.xpro_type = XPRO_LONG;
  129.       xo_c.xpro_value = tempsv.option_c;
  130.       xo_c.xpro_length = sizeof(tempsv.option_c);
  131.       option_ptrs[12] = &xo_c;
  132.       xo_m.xpro_description = "Maximum Block Size:";
  133.       xo_m.xpro_type = XPRO_LONG;
  134.       xo_m.xpro_value = tempsv.option_m;
  135.       xo_m.xpro_length = sizeof(tempsv.option_m);
  136.       option_ptrs[13] = &xo_m;
  137.       xo_n.xpro_description = "Send If Files = 0:";
  138.       xo_n.xpro_type = XPRO_BOOLEAN;
  139.       xo_n.xpro_value = tempsv.option_n;
  140.       xo_n.xpro_length = sizeof(tempsv.option_n);
  141.       option_ptrs[14] = &xo_n;
  142.       xo_l.xpro_description = "Block Change (Y,N,B,F):";
  143.       xo_l.xpro_type = XPRO_STRING;
  144.       xo_l.xpro_value = tempsv.option_l;
  145.       xo_l.xpro_length = sizeof(tempsv.option_l);
  146.       option_ptrs[15] = &xo_l;
  147.       xo_u.xpro_description = "Open Dos.Library:";
  148.       xo_u.xpro_type = XPRO_BOOLEAN;
  149.       xo_u.xpro_value = tempsv.option_u;
  150.       xo_u.xpro_length = sizeof(tempsv.option_u);
  151.       option_ptrs[16] = &xo_u;
  152.       xo_v.xpro_description = "ENV-Val Starter:";
  153.       xo_v.xpro_type = XPRO_STRING;
  154.       xo_v.xpro_value = tempsv.option_v;
  155.       xo_v.xpro_length = sizeof(tempsv.option_v);
  156.       option_ptrs[17] = &xo_v;
  157.  
  158.       /* Convert Y/N used elsewhere into "yes"/"no" required by spec */
  159.       for (i=6; i<=10; ++i) {
  160.         optr = option_ptrs[i];
  161.         strcpy(optr->xpro_value,(*optr->xpro_value == 'Y') ? "Yes" : "No");
  162.       }
  163.       optr = option_ptrs[14];
  164.       strcpy(optr->xpro_value,(*optr->xpro_value == 'Y') ? "Yes" : "No");
  165.       optr = option_ptrs[16];
  166.       strcpy(optr->xpro_value,(*optr->xpro_value == 'Y') ? "Yes" : "No");
  167.  
  168.       xpr_options(io,18L,option_ptrs);
  169.  
  170.       /* Convert "yes"/"no" or "on"/"off" into Y/N */
  171.       for (i=6; i<=10; ++i) {
  172.         optr = option_ptrs[i];
  173.         strcpy(optr->xpro_value,(!stricmp(optr->xpro_value,"Yes") || !stricmp(optr->xpro_value,"On")) ? "Y" : "N");
  174.       }
  175.       optr = option_ptrs[14];
  176.       strcpy(optr->xpro_value,(!stricmp(optr->xpro_value,"yes") || !stricmp(optr->xpro_value,"on")) ? "Y" : "N");
  177.       optr = option_ptrs[16];
  178.       strcpy(optr->xpro_value,(!stricmp(optr->xpro_value,"yes") || !stricmp(optr->xpro_value,"on")) ? "Y" : "N");
  179.  
  180.       /* Convert xpr_options() results into parseable options string */
  181.       sprintf(buf,"T%s,O%s,B%s,F%s,E%s,A%s,D%s,K%s,S%s,R%s,C%s,N%s,M%s,L%s,U%s,V%s,P%s",tempsv.option_t,
  182.         tempsv.option_o,tempsv.option_b,tempsv.option_f,tempsv.option_e,
  183.         tempsv.option_a,tempsv.option_d,tempsv.option_k,tempsv.option_s,
  184.         tempsv.option_r,tempsv.option_c,tempsv.option_n,tempsv.option_m,
  185.         tempsv.option_l,tempsv.option_u,tempsv.option_v,tempsv.option_p);
  186.     /* If xpr_options() not provided, try xpr_gets() instead */
  187.     } else {
  188.       /* Start buffer with current settings so user can see/edit them in place */
  189.       sprintf(buf,"T%s,O%s,B%s,F%s,E%s,A%s,D%s,K%s,S%s,R%s,C%s,N%s,M%s,L%s,U%s,V%s,P%s",sv->option_t,
  190.         sv->option_o,sv->option_b,sv->option_f,sv->option_e,
  191.         sv->option_a,sv->option_d,sv->option_k,sv->option_s,
  192.         sv->option_r,sv->option_c,sv->option_n,sv->option_m,
  193.         sv->option_l,sv->option_u,sv->option_v,sv->option_p);
  194.       if (io->xpr_gets)
  195.         xpr_gets(io,"ZedZap Options:",buf);
  196.     }
  197.   }
  198.   /* Upshift options string for easier parsing */
  199.   strupr(buf);
  200.  
  201.   /* Merge new T(ext) option into current settings if given */
  202.   /* "TY" = Force Text mode on,
  203.      "TN" = Force Text mode off,
  204.      "T?" = Use other end's text mode suggestion (default to binary)
  205.      "TC" = Ask Comm program for file type */
  206.   if (p = find_option(buf,'T')) {
  207.     if (*p == 'Y' || *p == 'N' || *p == '?' || *p == 'C') *sv->option_t = *p;
  208.     else ioerr(io,"Invalid T Flag Ignored; Should Be Y, N, ?, Or C");
  209.   }
  210.  
  211.   /* Merge new O(verwrite) option into current settings if given */
  212.   /* "OY" = Yes, delete old file and replace with new one,
  213.      "ON" = No, prevent overwrite by appending ".dup" to avoid name collision,
  214.      "OR" = Resume transfer at end of existing file,
  215.      "OS" = Skip file if it already exists; go on to next */
  216.   if (p = find_option(buf,'O')) {
  217.     if (*p == 'R' && !io->xpr_finfo) ioerr(io,"Can't Resume; xpr_finfo() not supported");
  218.     else if (*p == 'Y' || *p == 'N' || *p == 'R' || *p == 'S') *sv->option_o = *p;
  219.     else ioerr(io,"Invalid O Flag Ignored; Should Be Y, N, R, Or S");
  220.   }
  221.  
  222.   /* Merge new B(uffer) setting into current settings if given */
  223.   /* Size of file I/O buffer in kilobytes */
  224.   if (p = find_option(buf,'B')) {
  225.     len = atol(p);
  226.     if (len < 1) len = 1;
  227.     sprintf(sv->option_b,"%ld",len);
  228.   }
  229.  
  230.   /* Merge new F(ramelength) setting into other settings if given */
  231.   /* Number of bytes we're willing to send or receive between ACKs.
  232.      0 = unlimited; nonstop streaming data */
  233.   if (p = find_option(buf,'F')) {
  234.     len = atol(p);
  235.     if (len < 0) len = 0;
  236.     if (len > 0 && len < MINBLOCK) len = MINBLOCK;
  237.     sprintf(sv->option_f,"%ld",len);
  238.   }
  239.  
  240.   /* Merge new E(rror limit) setting into other settings if given */
  241.   /* Number of sequential errors which will cause an abort */
  242.   if (p = find_option(buf,'E')) {
  243.     len = atol(p);
  244.     if (len < 1)
  245.       len = 1;
  246.     if (len > 32767)
  247.       len = 32767;
  248.     sprintf(sv->option_e,"%ld",len);
  249.   }
  250.  
  251.   /* Merge new A(uto-activate) setting into other settings if given */
  252.   /* "AY" = Automatically call XProtocolReceive() if ZRQINIT string received
  253.      "AN" = Don't look for ZRQINIT; user will explicitly activate receive */
  254.   if (p = find_option(buf,'A')) {
  255.     if (*p == 'Y' || *p == 'N') *sv->option_a = *p;
  256.     else ioerr(io,"Invalid A Flag Ignored; Should Be Y Or N");
  257.   }
  258.  
  259.   /* Merge new D(elete after sending) setting into other options */
  260.   /* "DY" = Delete files after successfully sending them
  261.      "DN" = Don't delete files after sending */
  262.   if (p = find_option(buf,'D')) {
  263.     if (*p == 'Y' && (io->xpr_extension < 2 || !io->xpr_unlink))
  264.       ioerr(io,"Can't use DY; xpr_unlink() not supported");
  265.     else if (*p == 'Y' || *p == 'N') *sv->option_d = *p;
  266.     else ioerr(io,"Invalid D Flag Ignored; Should Be Y Or N");
  267.   }
  268.  
  269.   /* Merge new K(eep partial files) setting into other options */
  270.   /* "KY" = Keep partially-received file fragments to allow later resumption
  271.      "KN" = Delete partially-received file fragments */
  272.   if (p = find_option(buf,'K')) {
  273.     if (*p == 'N' && (io->xpr_extension < 2 || !io->xpr_unlink))
  274.       ioerr(io,"Can't use KN; xpr_unlink() not supported");
  275.     else if (*p == 'Y' || *p == 'N') *sv->option_k = *p;
  276.     else ioerr(io,"Invalid K Flag Ignored; Should Be Y Or N");
  277.   }
  278.  
  279.   /* Merge new S(end full path) setting into other options */
  280.   /* "SY" = Send full filename including directory path to receiver
  281.      "SN" = Send only simple filename portion, not including directory path */
  282.   if (p = find_option(buf,'S')) {
  283.     if (*p == 'Y' || *p == 'N') *sv->option_s = *p;
  284.     else ioerr(io,"Invalid S Flag Ignored; Should Be Y Or N");
  285.   }
  286.  
  287.   /* Merge new R(eceive path) setting into other options */
  288.   /* "RY" = Use full filename exactly as received; don't use P option path
  289.      "RN" = Ignore received directory path if any; use path from P option */
  290.   if (p = find_option(buf,'R')) {
  291.     if (*p == 'Y' || *p == 'N') *sv->option_r = *p;
  292.     else ioerr(io,"Invalid R Flag Ignored; Should Be Y Or N");
  293.   }
  294.  
  295.   /* Modify BPS Rate If We Have The BPS Rate Locked */
  296.   /* Limit this to 57.6k... The Amiga really can't safely go much faster... */
  297.   if (p = find_option(buf,'C')) {
  298.     len = atol(p);
  299.     if ((len < 300)||(len > 57600)) len = 0;
  300.     sprintf(sv->option_c,"%ld",len);
  301.   }
  302.  
  303.   /* Figure out if we should be able to send no files like ZedZap */
  304.   if (p = find_option(buf,'N')) {
  305.     if (*p == 'Y' || *p == 'N') *sv->option_n = *p;
  306.     else ioerr(io,"Invalid N Flag Ignored; Should Be Y Or N");
  307.   }
  308.  
  309.   /* Figure out the maximum block size that can be used... this must be */
  310.   /* below 8192 bytes */
  311.   if (p = find_option(buf,'M')) {
  312.     len = atol(p);
  313.     if (len == 0) len = 1024; /* default to regular zmodem block size */
  314.     if (len < MINBLOCK) len = MINBLOCK;
  315.     if (len > 8192) len = 8192;
  316.     sprintf(sv->option_m,"%ld",len);
  317.   }
  318.  
  319.   /* Merge new Fallback/forward option into current settings if given */
  320.   /* "LY" = Allow fallback and fallforward for block length
  321.      "LN" = Never change the block length
  322.      "LB" = Only fall back
  323.      "LF" = Only fall forward (???) Should not do anything */
  324.   if (p = find_option(buf,'L')) {
  325.     if (*p == 'Y' || *p == 'N' || *p == 'B' || *p == 'F') *sv->option_l = *p;
  326.     else ioerr(io,"Invalid L Flag Ignored; Should Be Y, N, B, Or F");
  327.   }
  328.  
  329.   /* Figure out if we can open the dos.library! While libraries are not*/
  330.   /* supposed to open it, we can do some pretty neat stuff if we do! */
  331.   if (p = find_option(buf,'U')) {
  332.     if (*p == 'Y' || *p == 'N') *sv->option_u = *p;
  333.     else ioerr(io,"Invalid U Flag Ignored; Should Be Y Or N");
  334.   }
  335.  
  336.   /* Read the starting string for all environment (ENV:) variables that */
  337.   /* the protocol will use. This requires the dos.library to be open!!! */
  338.   if (p = find_option(buf,'V')) {
  339.      len = 0;
  340.      bufback = sv->option_v; /* backup the starting position */
  341.      while((*p != ',')&&(*p != 0)&&(len < 29)){
  342.         *bufback = *p; /* copy a byte */
  343.         p++; /* move up one */
  344.         bufback++; /* move up one */
  345.         *bufback = 0; /* clear the following byte */
  346.         len++;
  347.      }
  348.   }
  349.  
  350.   /* Merge new P(ath) setting into other options */
  351.   /* "Pdir" = Receive files into directory "dir" if RN selected
  352.      "dir" can by any valid existing directory, with or without trailing "/" */
  353.   if (p = find_option(buf,'P')) {
  354.     strcpy(sv->option_p,p);
  355.     p = sv->option_p + strcspn(sv->option_p," ,\t\r\n");
  356.     *p = '\0';
  357.   }
  358.   Forbid(); /* no other program may change OPEN_LIBS now! */
  359.   OPEN_LIBS++;
  360.   if(*sv->option_u == 'Y'){
  361.      OpenDosLibrary();
  362.   }
  363.   Permit(); /* now its OK to change OPEN_LIBS */
  364.   return (*sv->option_a == 'Y') ? XPRS_SUCCESS|XPRS_NORECREQ|XPRS_HOSTMON : XPRS_SUCCESS|XPRS_NORECREQ;
  365. }
  366.  
  367.  
  368. /* Called by comm program to give us a chance to clean up before program ends */
  369. long __saveds XProtocolCleanup(struct XPR_IO *io) {
  370.   Forbid();    /* make sure that no programs add to OPEN_LIBS so that we */
  371.   OPEN_LIBS--; /* can safely close the dos.library if it is open         */
  372.   /* only the last program accessing the dos.library can safely close it */
  373.   /* without possibly crashing the system when other programs are trying */
  374.   /* to use dos.library functions                                        */
  375.   if(!OPEN_LIBS) CloseDosLibrary(); /* no more programs are prossibly    */
  376.                                     /* accessing the dos.library... at   */
  377.                                     /* least we assume so                */
  378.   Permit();
  379.   /* Release option memory, if any */
  380.   if (io->xpr_data) {
  381.     FreeMem(io->xpr_data,(long)sizeof(struct SetupVars));
  382.     io->xpr_data = NULL;
  383.   }
  384.  
  385.   return XPRS_SUCCESS;
  386. }
  387.  
  388.  
  389.  
  390. /* Called by comm program upon our request (XPRS_HOSTMON) to let us monitor
  391.    the incoming data stream for our receiver auto-activation string (ZRQINIT packet).
  392.    We only ask for this to be called if option AY is set. */
  393. long __saveds XProtocolHostMon(struct XPR_IO *io,UBYTE *serbuff,long actual,long maxsize) {
  394.   static UBYTE startrcv[] = { ZPAD, ZDLE, ZHEX, "00" };
  395.   struct SetupVars *sv;
  396.  
  397.   if (!(sv = (void *)io->xpr_data))
  398.     return actual;  /* XProtocolSetup() never called?! */
  399.  
  400.   if (!sv->matchptr)
  401.     sv->matchptr = startrcv;
  402.  
  403.   /* Scan through serbuff to see if we can match all bytes in the start string in sequence */
  404.   for (sv->bufpos=serbuff; sv->bufpos < serbuff+actual; ++sv->bufpos) {
  405.     if (*sv->bufpos == *sv->matchptr) {  /* if data matches current position in match string */
  406.       ++sv->matchptr;          /* increment match position */
  407.       if (!*sv->matchptr) {    /* if at end of match string, it all matched */
  408.         sv->buflen = actual - (sv->bufpos - serbuff);
  409.         XProtocolReceive(io);
  410.         sv->matchptr = startrcv;
  411.         actual = 0;
  412.         break;
  413.       }
  414.     } else if (sv->matchptr > startrcv) { /* if mismatch, reset to start of match string */
  415.       sv->matchptr = startrcv;
  416.       if (*sv->bufpos == *sv->matchptr)
  417.         ++sv->matchptr;
  418.     }
  419.   }
  420.  
  421.   sv->bufpos = NULL;
  422.   return actual;
  423. }
  424.  
  425.  
  426. /* Called by comm program to let us monitor user's inputs; we never ask for
  427.    this to be called, but it's better to recover gracefully than guru the machine */
  428. long __saveds XProtocolUserMon(struct XPR_IO *io,UBYTE *serbuff,long actual,long maxsize) {
  429.   return actual;
  430. }
  431.  
  432.  
  433.  
  434. /* Perform setup and initializations common to both Send and Receive routines */
  435. struct Vars *setup(struct XPR_IO *io) {
  436.   static long bauds[] = { 300,600,1200,2400,4800,7200,9600,12000,14400,16800,19200,31250,38400,57600,76800,115200 };
  437.   struct SetupVars *sv;
  438.   struct Vars *v;
  439.   long origbuf, newstatus;
  440. #ifdef DEBUGLOG
  441.   long i, *lng;
  442. #endif
  443.  
  444.   /* Make sure comm program supports the required call-back functions */
  445.   if (!io->xpr_update) return NULL;
  446.   if (!io->xpr_fopen || !io->xpr_fclose || !io->xpr_fread || !io->xpr_fwrite ||
  447.       !io->xpr_fseek || !io->xpr_sread || !io->xpr_swrite) {
  448.     ioerr(io,"Comm Prog Missing Required Function(s); See Docs");
  449.     return NULL;
  450.   }
  451.  
  452.   /* Hook in default transfer options if XProtocolSetup wasn't called */
  453.   if (!(sv = (void *)io->xpr_data)) {
  454.     io->xpr_data = AllocMem((long)sizeof(struct SetupVars),MEMF_CLEAR);
  455.     if (!(sv = (void *)io->xpr_data)) {
  456.       ioerr(io,"Not Enough Memory");
  457.       return NULL;
  458.     }
  459.     *sv = Default_Config;
  460.   }
  461.  
  462.   /* Allocate memory for our unshared variables, to provide reentrancy */
  463.   if (!(v = AllocMem((long)sizeof(struct Vars),MEMF_CLEAR))) {
  464. nomem:
  465.     ioerr(io,"Not Enough Memory");
  466.     return NULL;
  467.   }
  468.   v->Modemchar = v->Modembuf;
  469.  
  470.   /* Allocate memory for our file I/O buffer; if we can't get as much as
  471.      requested, keep asking for less until we hit minimum before giving up */
  472.   v->Filebufmax = origbuf = atol(sv->option_b) * 1024;
  473.   while (!(v->Filebuf = AllocMem(v->Filebufmax,0L))) {
  474.     if (v->Filebufmax > 1024) v->Filebufmax -= 1024;
  475.     else {
  476.       FreeMem(v,(long)sizeof(struct Vars));
  477.       goto nomem;
  478.     }
  479.   }
  480.  
  481.   /* maximum block size */
  482.   v->KSIZE = atol(sv->option_m);
  483.   /* new bps rate */
  484.   v->NEWBAUD = atol(sv->option_c);
  485.   /* send if there are no files */
  486.   if(*sv->option_n == 'Y'){v->USEZERO = TRUE;}
  487.   else{v->USEZERO = FALSE;}
  488.   /* open dos library */
  489.   if(*sv->option_u == 'Y'){v->use_dos_lib = 1;}
  490.   else{v->use_dos_lib = 0;}
  491.   /* fallback/forward */
  492.   if(*sv->option_l == 'Y'){v->fall_back = 1;v->fall_forward = 1;}
  493.   if(*sv->option_l == 'N'){v->fall_back = 0;v->fall_forward = 0;}
  494.   if(*sv->option_l == 'B'){v->fall_back = 1;v->fall_forward = 0;}
  495.   if(*sv->option_l == 'F'){v->fall_back = 0;v->fall_forward = 1;}
  496.   /* environment variable starter */
  497.   strcpy(v->env_starter,sv->option_v);
  498.  
  499.   if((v->KSIZE * 2) > v->Filebufmax){
  500.      if(v->Filebufmax >=  1024) {v->KSIZE = 512 ;strcpy(sv->option_m,"512" );}
  501.      if(v->Filebufmax >=  2048) {v->KSIZE = 1024;strcpy(sv->option_m,"1024");}
  502.      if(v->Filebufmax >=  4096) {v->KSIZE = 2048;strcpy(sv->option_m,"2048");}
  503.      if(v->Filebufmax >=  8192) {v->KSIZE = 4096;strcpy(sv->option_m,"4096");}
  504.      if(v->Filebufmax >= 16384) {v->KSIZE = 8192;strcpy(sv->option_m,"8192");}
  505.   }
  506.  
  507.   /* If framelength was intended to match buffer size, stay in sync */
  508.   v->Tframlen = atol(sv->option_f);
  509.   if (v->Tframlen && v->Tframlen == origbuf) v->Tframlen = v->Filebufmax;
  510.  
  511.   v->ErrorLimit = atol(sv->option_e);
  512.  
  513.   /* Copy caller's io struct into our Vars for easier passing */
  514.   v->io = *io;
  515.  
  516. #ifdef DEBUGLOG
  517.   if (!DebugLog) DebugLog = xpr_fopen(&v->io,DebugName,"w");
  518.   dlog(v,"XPR_IO struct:\n");
  519.   for (i=0,lng=(long *)io; i < sizeof(struct XPR_IO)/4; ++i) {
  520.     sprintf(v->Msgbuf,"  %08lx\n",*lng++);
  521.     dlog(v,v->Msgbuf);
  522.   }
  523. #endif
  524.  
  525.   /* Get baud rate; set serial port mode if necessary (and possible) */
  526.   if (v->io.xpr_setserial) {
  527.     v->Oldstatus = xpr_setserial(&v->io,-1L);
  528.     if (v->Oldstatus != -1) {
  529.       /* ZModem requires 8 data bits, no parity (full transparency),
  530.          leave other settings alone */
  531.       newstatus = v->Oldstatus & 0xFFFFE0BC;
  532.       /* newstatus |= on_flags; Here's where we'd turn bits on if we needed to */
  533.       if (newstatus != v->Oldstatus)
  534.         xpr_setserial(&v->io,newstatus);
  535.       v->Baud = bauds[(newstatus>>16) & 0xFF];
  536. #ifdef DEBUGLOG
  537.       sprintf(v->Msgbuf,"Old Serial Status = %lx, New = %lx, Baud = %ld\n",v->Oldstatus,newstatus,v->Baud);
  538.       dlog(v,v->Msgbuf);
  539. #endif
  540.     } else
  541.       v->Baud = 2400;
  542.   /* If no xpr_setserial(), muddle along with most likely guess */
  543.   } else v->Baud = 2400;
  544.  
  545.   return v;
  546. }
  547.  
  548.  
  549.  
  550. /* Set text/binary mode flags in accordance with T option setting */
  551. void set_textmode(struct Vars *v) {
  552.   struct SetupVars *sv;
  553.   long i;
  554.  
  555.   sv = (void *)v->io.xpr_data;
  556.   switch(*sv->option_t) {
  557.     case 'Y':  /* Force text mode on receive; suggest text mode on send */
  558. TY:   v->Rxascii = TRUE;
  559.       v->Rxbinary = FALSE;
  560.       v->Lzconv = ZCNL;
  561.       break;
  562.     case 'N':  /* Force binary mode on receive; suggest binary mode on send */
  563. TN:   v->Rxascii = FALSE;
  564.       v->Rxbinary = TRUE;
  565.       v->Lzconv = ZCBIN;
  566.       break;
  567.     case 'C':  /* Ask comm program for proper mode for this file */
  568.       if (v->io.xpr_finfo) {
  569.         i = xpr_finfo(&v->io,v->Filename,2L);
  570.         if (i == 1) goto TN;  /* Comm program says use binary mode */
  571.         if (i == 2) goto TY;  /* Comm program says use text mode */
  572.       }
  573.       /* xpr_finfo() not provided (or failed); default to T? */
  574.     case '?':
  575.       v->Rxascii = v->Rxbinary = FALSE;
  576.       v->Lzconv = 0;
  577.       break;
  578.   }
  579. }
  580.  
  581.  
  582.  
  583. /* Search for specified option setting in string */
  584. UBYTE *find_option(UBYTE *buf,UBYTE option) {
  585.   while (*buf) {
  586.     buf += strspn(buf," ,\t\r\n");
  587.     if (*buf == option) return ++buf;
  588.     buf += strcspn(buf," ,\t\r\n");
  589.   }
  590.  
  591.   return NULL;
  592. }
  593.  
  594.  
  595.  
  596. /* send cancel string to get the other end to shut up */
  597. void canit(struct Vars *v) {
  598.   static char canistr[] = { 24,24,24,24,24,24,24,24,24,24,8,8,8,8,8,8,8,8,8,8,0 };
  599.  
  600.   zmputs(v,canistr);
  601. }
  602.  
  603.  
  604.  
  605. /* Send a string to the modem, with processing for \336 (sleep 1 sec)
  606.    and \335 (break signal, ignored since XPR spec doesn't support it) */
  607. void zmputs(struct Vars *v,UBYTE *s) {
  608.   UBYTE c;
  609.  
  610.   while (*s) {
  611.     switch (c = *s++) {
  612.       case '\336':
  613.         TimeOut(50L);
  614.       case '\335':
  615.         break;
  616.       default:
  617.         sendline(v,c);
  618.     }
  619.   }
  620.   sendbuf(v);
  621. }
  622.  
  623.  
  624.  
  625. /* Write one character to the modem */
  626. void xsendline(struct Vars *v,UBYTE c) {
  627.   v->Outbuf[v->Outbuflen++] = c;
  628.   if (v->Outbuflen >= sizeof(v->Outbuf))
  629.     sendbuf(v);
  630. }
  631.  
  632.  
  633.  
  634. /* Send any data waiting in modem output buffer */
  635. void sendbuf(struct Vars *v) {
  636.   if (v->Outbuflen) {
  637.     xpr_swrite(&v->io,v->Outbuf,(long)v->Outbuflen);
  638.     v->Outbuflen = 0;
  639.   }
  640. }
  641.  
  642.  
  643.  
  644. /* Get a byte from the modem;
  645.    return TIMEOUT if no read within timeout tenths of a second,
  646.    return RCDO if carrier lost or other fatal error (sread returns -1).
  647.    Added in some buffering so we wouldn't hammer the system with single-byte
  648.    serial port reads.  Also, the buffering makes char_avail() a lot easier to implement. */
  649. short readock(struct Vars *v,short tenths) {
  650.   long t;
  651.  
  652.   /* If there's data waiting in our buffer, return next byte */
  653.   if (v->Modemcount) {
  654. gotdata:
  655.     --v->Modemcount;
  656.     return (short)(*v->Modemchar++);
  657.   }
  658.  
  659.   /* Our buffer is empty; see if there's anything waiting in system buffer.
  660.      If the caller is in a hurry, don't wait around, but if it can spare
  661.      a half second, wait a bit and build up some input so we don't do as
  662.      many sread() calls. */
  663.   t = (tenths < 5) ? 0 : 500000;
  664. #ifdef DEBUGLOG
  665.   sprintf(v->Msgbuf,"Input Buffer Empty; Calling sread For %ld Bytes, %ld usec\n",(long)sizeof(v->Modembuf),t);
  666.   dlog(v,v->Msgbuf);
  667. #endif
  668.   v->Modemcount = xpr_sread(&v->io,v->Modembuf,(long)sizeof(v->Modembuf),t);
  669. #ifdef DEBUGLOG
  670.   sprintf(v->Msgbuf,"   sread Returned %ld\n",v->Modemcount);
  671.   dlog(v,v->Msgbuf);
  672. #endif
  673.   if (v->Modemcount < 0) {  /* Carrier dropped or other fatal error; abort */
  674.     v->Modemcount = 0;
  675.     return RCDO;
  676.   } else if (!v->Modemcount) {   /* Nothing in system buffer; try waiting */
  677.     t = tenths * 100000L - t;
  678. #ifdef DEBUGLOG
  679.     sprintf(v->Msgbuf,"   Calling sread For 1 Byte, %ld usec\n",t);
  680.     dlog(v,v->Msgbuf);
  681. #endif
  682.     v->Modemcount = xpr_sread(&v->io,v->Modembuf,1L,t);
  683. #ifdef DEBUGLOG
  684.   sprintf(v->Msgbuf,"   sread Returned %ld\n",v->Modemcount);
  685.   dlog(v,v->Msgbuf);
  686. #endif
  687.     if (v->Modemcount < 0) {
  688.       v->Modemcount = 0;
  689.       return RCDO;
  690.     } else if (!v->Modemcount) return TIMEOUT;  /* Nothing received in time */
  691.   }
  692.   v->Modemchar = v->Modembuf;  /* Reset buffer pointer to start of data */
  693.   goto gotdata;
  694. }
  695.  
  696.  
  697.  
  698. /* Check if there's anything available to read from the modem */
  699. char char_avail(struct Vars *v) {
  700.   if (v->Modemcount) return TRUE;
  701.  
  702.   /* No data in our buffer; check system's input buffer */
  703.   v->Modemcount = xpr_sread(&v->io,v->Modembuf,(long)sizeof(v->Modembuf),0L);
  704.   if (v->Modemcount < 1) {  /* Nothing in system buffer either */
  705.     v->Modemcount = 0;
  706.     return FALSE;
  707.   } else {                  /* System buffer had something waiting for us */
  708.     v->Modemchar = v->Modembuf;
  709.     return TRUE;
  710.   }
  711. }
  712.  
  713.  
  714.  
  715. /* Update the elapsed time, expected total time, and effective data
  716.    transfer rate values for status display */
  717. void update_rate(struct Vars *v) {
  718.   ULONG sent, elapsed, expect;
  719.   short hr, min;
  720.   struct timeval tv;
  721.  
  722.   /* Compute effective data rate so far in characters per second */
  723.   sent = v->xpru.xpru_bytes - v->Strtpos;
  724.   GetSysTimeXPR(&tv);
  725.   elapsed = (tv.tv_secs & 0x7FFFFF) * 128 + tv.tv_micro / 8192;
  726.   elapsed -= (v->Starttime.tv_secs & 0x7FFFFF) * 128 + v->Starttime.tv_micro / 8192;
  727.   if (elapsed < 128) elapsed = 128;
  728.   /* If we haven't transferred anything yet (just starting), make reasonable
  729.      guess (95% throughput); otherwise, compute actual effective transfer rate */
  730.   if(v->NEWBAUD){
  731.      v->xpru.xpru_datarate = (sent) ? (sent * 128 / elapsed) : (v->NEWBAUD * 95 / 1000);
  732.   }
  733.   else{
  734.      v->xpru.xpru_datarate = (sent) ? (sent * 128 / elapsed) : (v->Baud * 95 / 1000);
  735.   }
  736.   /* Compute expected total transfer time based on data rate so far */
  737.   if (v->xpru.xpru_filesize < 0)
  738.     expect = 0; /* Don't know filesize; display time=0 */
  739.   else
  740.     expect = (v->xpru.xpru_filesize - v->Strtpos) / v->xpru.xpru_datarate;
  741.   hr = expect / 3600;   /* How many whole hours */
  742.   expect -= hr * 3600;  /* Remainder not counting hours */
  743.   min = expect / 60;    /* How many whole minutes */
  744.   expect -= min * 60;   /* Remaining seconds */
  745.   sprintf(v->Msgbuf,"%02ld:%02ld:%02ld",(long)hr,(long)min,expect);
  746.   v->xpru.xpru_expecttime = (char *)v->Msgbuf;
  747.  
  748.   /* Compute elapsed time for this transfer so far */
  749.   elapsed /= 128;
  750.   hr = elapsed / 3600;
  751.   elapsed -= hr * 3600;
  752.   min = elapsed / 60;
  753.   elapsed -= min * 60;
  754.   sprintf(v->Msgbuf+20,"%02ld:%02ld:%02ld",(long)hr,(long)min,elapsed);
  755.   v->xpru.xpru_elapsedtime = (char *)v->Msgbuf+20;
  756. }
  757.  
  758.  
  759.  
  760. /* Buffered file I/O fopen() interface routine */
  761. void *bfopen(struct Vars *v,UBYTE *mode) {
  762.   if(v->File) bfclose(v); /* Why was the file still open? ARGH! */
  763.   /* Initialize file-handling variables */
  764.   v->Filebufpos = v->Filebuflen = v->Filebufcnt = 0;
  765.   v->Fileflush = FALSE;
  766.   v->Filebufptr = v->Filebuf;
  767.   /* Open the file */
  768. #ifdef DEBUGLOG
  769.   sprintf(v->Msgbuf,"bfopen: %s %s\n",v->Filename,mode);
  770.   dlog(v,v->Msgbuf);
  771. #endif
  772.   return (v->File = xpr_fopen(&v->io,v->Filename,mode));
  773. }
  774.  
  775.  
  776.  
  777. /* Buffered file I/O fclose() interface routine */
  778. void bfclose(struct Vars *v) {
  779.   if (v->File) {
  780.     /* If bfwrite() left data lingering in buffer, flush it out before closing */
  781.     if (v->Fileflush)
  782.       xpr_fwrite(&v->io,v->Filebuf,1L,v->Filebufcnt,v->File);
  783.     /* Close the file */
  784.     xpr_fclose(&v->io,v->File);
  785.     v->File = NULL;
  786.   }
  787. }
  788.  
  789.  
  790.  
  791. /* Buffered file I/O fseek() interface routine */
  792. void bfseek(struct Vars *v,long pos) {
  793.   long offset;
  794.  
  795.   /* If new file position is within currently buffered section, reset pointers */
  796.   if (pos >= v->Filebufpos && pos < v->Filebufpos + v->Filebuflen) {
  797.     offset = pos - v->Filebufpos;
  798.     v->Filebufptr = v->Filebuf + offset;
  799.     v->Filebufcnt = v->Filebuflen - offset;
  800.   /* Otherwise, fseek() file and discard buffer contents to force new read */
  801.   } else {
  802.     xpr_fseek(&v->io,v->File,pos,0L);
  803.     v->Filebuflen = v->Filebufcnt = 0;
  804.     v->Filebufpos = pos;
  805.   }
  806. }
  807.  
  808.  
  809.  
  810. /* Buffered file I/O fread() interface routine */
  811. long bfread(struct Vars *v,UBYTE *buf,long length) {
  812.   long count, total = 0;
  813.  
  814.   /* Keep going until entire request completed */
  815.   while (length > 0) {
  816.     /* Copy as much of the request as possible from the buffer */
  817.     count = (length <= v->Filebufcnt) ? length : v->Filebufcnt;
  818.     CopyMem(v->Filebufptr,buf,count);
  819. #ifdef DEBUGLOG
  820.     sprintf(v->Msgbuf,"bfread Got %ld Bytes From Buffer\n",count);
  821.     dlog(v,v->Msgbuf);
  822. #endif
  823.     buf += count;
  824.     total += count;
  825.     length -= count;
  826.     v->Filebufptr += count;
  827.     v->Filebufcnt -= count;
  828.  
  829.     /* If we've emptied the buffer, read next buffer's worth */
  830.     if (!v->Filebufcnt) {
  831.       v->Filebufpos += v->Filebuflen;
  832.       v->Filebufptr = v->Filebuf;
  833.       v->Filebufcnt = v->Filebuflen = xpr_fread(&v->io,v->Filebuf,1L,v->Filebufmax,v->File);
  834. #ifdef DEBUGLOG
  835.       sprintf(v->Msgbuf,"bfread Read %ld Bytes\n",v->Filebufcnt);
  836.       dlog(v,v->Msgbuf);
  837. #endif
  838.       /* If we hit the EOF, return with however much we read so far */
  839.       if (!v->Filebufcnt) break;
  840.     }
  841.   }
  842.  
  843.   return total;
  844. }
  845.  
  846.  
  847.  
  848. /* Buffered file I/O fwrite() interface routine */
  849. long bfwrite(struct Vars *v,UBYTE *buf,long length) {
  850.   long count, total = 0;
  851.  
  852.   /* Keep going until entire request completed */
  853.   while (length > 0) {
  854.     /* Copy as much as will fit into the buffer */
  855.     count = v->Filebufmax - v->Filebufcnt;
  856.     if (length < count) count = length;
  857.     CopyMem(buf,v->Filebufptr,count);
  858. #ifdef DEBUGLOG
  859.     sprintf(v->Msgbuf,"bfwrite Buffered %ld Bytes\n",count);
  860.     dlog(v,v->Msgbuf);
  861. #endif
  862.     buf += count;
  863.     total += count;
  864.     length -= count;
  865.     v->Filebufptr += count;
  866.     v->Filebufcnt += count;
  867.     v->Fileflush = TRUE;
  868.  
  869.     /* If we've filled the buffer, write it out */
  870.     if (v->Filebufcnt == v->Filebufmax) {
  871.       count = xpr_fwrite(&v->io,v->Filebuf,1L,v->Filebufcnt,v->File);
  872. #ifdef DEBUGLOG
  873.       sprintf(v->Msgbuf,"bfwrite Wrote %ld Bytes\n",count);
  874.       dlog(v,v->Msgbuf);
  875. #endif
  876.       if (count < v->Filebufcnt) return -1;
  877.       v->Filebufptr = v->Filebuf;
  878.       v->Filebufcnt = 0;
  879.       v->Fileflush = FALSE;
  880.     }
  881.   }
  882.  
  883.   return total;
  884. }
  885.  
  886.  
  887.  
  888. /* Have the comm program display an error message for us, using a
  889.    temporary XPR_UPDATE structure; used to display errors before Vars
  890.    gets allocated */
  891. void ioerr(struct XPR_IO *io,char *msg) {
  892.   struct XPR_UPDATE xpru;
  893.  
  894.   if (io->xpr_update) {
  895.     xpru.xpru_updatemask = XPRU_ERRORMSG;
  896.     xpru.xpru_errormsg = msg;
  897.     xpr_update(io,&xpru);
  898.   }
  899. }
  900.  
  901.  
  902.  
  903. /* Have the comm program display an error message for us, using the
  904.    normal XPR_IO structure allocated in Vars */
  905. void upderr(struct Vars *v,char *msg) {
  906.   v->xpru.xpru_updatemask = XPRU_ERRORMSG;
  907.   v->xpru.xpru_errormsg = msg;
  908.   if (msg == v->Msgbuf) /* Ensure message length < 50 */
  909.     msg[48] = '\0';
  910.   UpdateStats(v);
  911.   xpr_update(&v->io,&v->xpru);
  912. #ifdef DEBUGLOG
  913.   dlog(v,msg);
  914.   dlog(v,"\n");
  915. #endif
  916. }
  917.  
  918.  
  919.  
  920. /* Have the comm program display a normal message for us */
  921. void updmsg(struct Vars *v,char *msg) {
  922.   v->xpru.xpru_updatemask = XPRU_MSG;
  923.   v->xpru.xpru_msg = msg;
  924.   if (msg == v->Msgbuf) /* Ensure message length < 50 */
  925.     msg[48] = '\0';
  926.   UpdateStats(v);
  927.   xpr_update(&v->io,&v->xpru);
  928. #ifdef DEBUGLOG
  929.   dlog(v,msg);
  930.   dlog(v,"\n");
  931. #endif
  932. }
  933.  
  934. /* Set the pass/fail status of this file */
  935. void updstatus(struct Vars *v,char *filename,long status) {
  936.   v->xpru.xpru_updatemask = XPRU_FILENAME|XPRU_STATUS;
  937.   v->xpru.xpru_filename = filename;
  938.   v->xpru.xpru_status = status;
  939.   UpdateStats(v);
  940.   xpr_update(&v->io,&v->xpru);
  941. }
  942.  
  943. /* Figure out how many bytes are free on the drive we're uploading to.
  944.    Stubbed out for now; not supported by XPR spec. */
  945. long getfree(void) {
  946.   return 0x7FFFFFFF;
  947. }
  948.  
  949. /* Check whether file already exists; used to detect potential overwrites */
  950. char exist(struct Vars *v) {
  951.   void *file;
  952.  
  953.   file = xpr_fopen(&v->io,v->Filename,"r");
  954.   if (file) {
  955.     xpr_fclose(&v->io,file);
  956.     return TRUE;
  957.   } else return FALSE;
  958. }
  959.  
  960. #ifdef DEBUGLOG
  961. /* Write a message to the debug log */
  962. void dlog(struct Vars *v,UBYTE *s) {
  963.   /* Open the debug log if it isn't already open */
  964.   if (!DebugLog) DebugLog = xpr_fopen(&v->io,DebugName,"a");
  965.   xpr_fwrite(&v->io,s,1L,(long)strlen(s),DebugLog);
  966.   /* Close file to flush output buffer; comment these two lines out if
  967.      you aren't crashing your system and don't mind waiting until the
  968.      transfer finishes to look at your log file.
  969.   xpr_fclose(&v->io,DebugLog);
  970.   DebugLog = NULL; */
  971. }
  972. #endif
  973.